Amazon Lexでスロットのバリデーションを行う
渡辺です。
Amazon Lexのスロットを理解するでは、Lexボットでのスロットについて紹介しました。 スロットとは、ユーザから入力されるパラメータのセットです。 例えば、予約インテントであれば、予約日と予約時間が必須パラメータとしてスロットに定義します。
Lexでは、必要なパラメータが揃うまでユーザにプロンプトを表示し、パラメータが揃った時点でLambdaを呼び出すことができます。 これは自動化されており非常に便利です。 一方、細かい制御、例えば値のバリデーション(妥当性チェック)を行いたい場合にはカスタマイズが必要です。 今回は、Lexボットでスロットのバリデーションを紹介します。
バリデーション関数の設定
はじめに、以下のコードでLambda関数(Node.js 6.10)を作成し、バリデーション関数として設定しましょう。
exports.handler = function (event, context, callback) { console.log(JSON.stringify(event)); var response = { sessionAttributes: event.sessionAttributes, dialogAction: { type: 'Delegate', slots: event.currentIntent.slots } }; callback(null, response); };
続けて、Lexボットのインテントでバリデーションを有効にし、Lambda関数を選択します。
これで、ユーザがスロットの値を入力後、Lexが処理する前にLambda関数が呼ばれるようになります。 現在の実装は、Lambdaで何もせずにLexに処理を委譲(Delegate)している状態です。
入力イベントのスロット情報
Lambdaでは、Lexボットからの入力をJSON形式でイベントを受けます。 この入力イベントには、次のように、スロット情報やセッション情報が含まれます。
{ "currentIntent": { "name": "intent-name", "slots": { "slot name": "value", "slot name": "value" }, "slotDetails": { "slot name": { "resolutions" : [ { "value": "resolved value" }, { "value": "resolved value" } ], "originalValue": "original text" }, "slot name": { "resolutions" : [ { "value": "resolved value" }, { "value": "resolved value" } ], "originalValue": "original text" } }, "confirmationStatus": "None, Confirmed, or Denied (intent confirmation, if configured)", }, "sessionAttributes": { "key": "value", "key": "value" } }
入力イベントから必要なスロット値を取得し、バリデーション(妥当性チェック)を行いましょう。
slots
currentIntent.slots
には、スロットの情報が「スロット名: 値」の形式で含まれます。
例えば、DateとTimeがスロット値として定義されているならば、次のようになるでしょう。
"slots": { "Date": "2017-1-25", "Time": null }
ここではDateはユーザが入力済みなため日付けが設定されていますが、Timeは未入力なのでnullが設定されています。 単純なバリデーションであれば、slotsの値だけで判定できます。
slotDetails
currentIntent.slotDetails
には、スロットの詳細情報が含まれます。
originalValue
にはユーザが入力したテキストが、そのまま含まれます。
Lexでは、例えば、Dateタイプで「明日」というユーザの入力があった場合、自動的に日付け計算を行い、例えば「2018-1-26」という日付けに変換する機能があります。
ユーザが入力した元の値が、originalValue
です。
resolutions
配は、スロットで認識された値のリストです。
originalValue
からどのように値を解決したのかに関する情報が5個まで含まれます。
出力イベントのdialogAction
レスポンスとなる出力イベントのdialogAction
では、次にユーザが入力する情報についてのヒントをLexに返します。
今回はバリデーションを失敗時に、dialogAction.type
にElicitSlot
を指定し、ユーザに再入力を求めます。
バリデーションが不要な場合や妥当な値だった場合は、Delegate
を指定し、Lexに処理を委譲します(おまかせモード)。
{ "sessionAttributes": { "key1": "value1", "key2": "value2" ... }, "dialogAction": { "type": "ElicitIntent, ElicitSlot, ConfirmIntent, Delegate, or Close", Full structure based on the type field. See below for details. } }
妥当性チェックを実装する
実装してみましょう。 ここでは予約可能時間が10時から17時までとしてみます(微妙にバグってますが気にしないでください)。
exports.handler = function(event, context, callback) { console.log(JSON.stringify(event)); var time = event.currentIntent.slots.Time; if (time != null && !isValidateTime(time)) { event.currentIntent.slots.Time = null; var response = { sessionAttributes: event.sessionAttributes, dialogAction: { type: 'ElicitSlot', message: { contentType: "PlainText", content: "Sorry, it is out of business hours.\nYou can reserve from 10:00 to 17:00." }, intentName: event.currentIntent.name, slotToElicit: 'Time', slots: event.currentIntent.slots } }; callback(null, response); } else { var response = { sessionAttributes: event.sessionAttributes, dialogAction: { type: 'Delegate', slots: event.currentIntent.slots } }; callback(null, response); } }; const isValidateTime = function(time) { var hour = Number(time.split(':')[0]); return 10 <= hour && hour <= 17; }
dialogAction
でElicitSlot
を指定する場合、intentName
, slotToElicit
, slots
が必須です。
intentName
は、今回、インテントが変わるわけではないため、現在のインテント名で問題ありません。。
slotToElicit
は、入力を促すスロット名(Time)を指定します。
slots
には不正な値をクリアするため、未入力(null)にしておきます。
メッセージは必須ではありませんが、営業時間外であることを伝え、営業時間を入力するように促します。 未指定の場合、Lexボットのスロット定義にあるプロンプトが表示されます。
動作確認
それでは動作確認してみます。
しっかりと、バリデーションが効いていることが確認出来ました。
まとめ
インターフェイスを作成するならば、値のバリデーションは必要不可欠です。 Lexでは必要最小限のコードで、バリデーションを行い、良い感じにユーザに入力を促せることができます。 曜日判定なども行えば、より洗練された予約ボットとなるでしょう。 24/365で受付を行うブラックボットは作らないでくださいね。